/**
 * 分数类
 * by littlefean
 */
class FractionNumber {

    /**
     * 构建一个分数
     * @param up {Number}
     * @param down {Number}
     */
    constructor(up, down = 1) {
        this.up = up;
        this.down = down;
    }

    copy() {
        return new FractionNumber(this.up, this.down);
    }

    /**
     * 取倒数
     */
    reverse() {
        let temp = this.up;
        this.up = this.down;
        this.down = temp;
    }

    /**
     * 分子分母约分化简
     * in-place
     */
    simplify() {
        if (this.up === 0 || this.down === 1) {
            this.down = 1;
            return;
        }
        // 正负号处理
        if (this.up < 0 && this.down < 0) {
            this.up *= -1;
            this.down *= -1;
        } else if (this.down < 0) {
            // 负号从分母换到分子上来
            this.down *= -1;
            this.up *= -1;
        }
        // 约分
        let g = gcd(this.up, this.down);
        this.up = Math.floor(this.up / g);
        this.down = Math.floor(this.down / g);
    }

    toString() {
        return `${this.up}/${this.down}`;
    }

    toHtml() {
        if (this.down === 1) {
            return `<div class="R">${this.up}</div>`;
        } else {
            return `<div class="R"><div class="up">${this.up}</div><hr><div class="down">${this.down}</div></div>`;
        }
    }

    /**
     * 把一个数字字符串解析成分数对象
     * @param string {String}
     * @return {FractionNumber}
     */
    static evalString(string) {
        if (string.includes(".")) {
            let z = new this(+string.split(".")[0]);  // 整数部分
            let x = string.split(".")[1];  // 小数部分
            let xF = new this(+x, 10 ** x.length);
            z.added(xF);
            return z;
        } else {
            return new this(+string);
        }
    }

    /**
     * inPlace
     * @param nf {FractionNumber}
     */
    added(nf) {
        let l = lcm(this.down, nf.down);
        let thisM = Math.floor(l / this.down);
        this.down *= thisM;
        this.up *= thisM;
        let nfM = Math.floor(l / nf.down);
        this.up += nf.up * nfM;
        this.simplify();
    }

    /**
     *
     * @param nf {FractionNumber}
     */
    muled(nf) {
        this.up *= nf.up;
        this.down *= nf.down;
        this.simplify();
    }

    mul(nf) {
        let self = this.copy();
        self.muled(nf);
        return self;
    }

    add(nf) {
        let self = this.copy();
        self.added(nf);
        return self;
    }

}

function gcd(n1, n2) {
    return n2 > 0 ? gcd(n2, n1 % n2) : n1;
}

function lcm(n1, n2) {
    return Math.floor(n1 * n2 / gcd(n1, n2));
}


